package homework2.NearestPoints;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;

import javax.swing.JButton;
import javax.swing.JInternalFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

/**
 * Demo for manual specify points in the panel via mouse click
 * 
 * @author eDisOn 2009212634 lilh09@mails.tsinghua.edu.cn
 * 
 */
public class ManuNearestPointsFrame extends JInternalFrame
{
	/**
	 * serialVersionUID
	 */
	private static final long serialVersionUID = -8222216569063363147L;

	/* Used to specify ManuNearestPointsFrame location and size */
	private static final int CASCADE_WIDTH = 10;
	private static final int LOCATION_X = 0;
	private static final int LOCATION_Y = 0;
	private static final int SIZE_X = 600;
	private static final int SIZE_Y = 400;

	private PointsPanel paintPanel;
	private JPanel runPanel;
	private JButton runButton;
	private int index = 0;

	public ManuNearestPointsFrame(int initIndex)
	{
		super("Manual Specified Nearest Points Frame", true, true, true, true);
		index = initIndex;
		init();
	}

	private void init()
	{
		paintPanel = new PointsPanel();
		add(paintPanel, BorderLayout.CENTER);

		runPanel = new JPanel();
		runPanel.setLayout(new FlowLayout());
		runPanel.setBackground(Color.LIGHT_GRAY);
		runButton = new JButton("RUN");
		runButton.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				if (paintPanel.getPoints().size() < 2)
				{
					JOptionPane.showMessageDialog(ManuNearestPointsFrame.this,
							"You should specify two points at least!");
				}
				else
				{
					CalcNearestPoints calculator = new CalcNearestPoints();
					ArrayList<NearestPoints> nearestPointsArr = calculator
							.exec(paintPanel.getPoints());
					JOptionPane.showMessageDialog(ManuNearestPointsFrame.this,
							calculator.getResult(nearestPointsArr));
					paintPanel.drawLine(nearestPointsArr);
				}
			}
		});
		runPanel.add(runButton);
		add(runPanel, BorderLayout.SOUTH);

		/* Set properties of this frame */
		setBounds(LOCATION_X + CASCADE_WIDTH * index, LOCATION_Y
				+ CASCADE_WIDTH * index, SIZE_X, SIZE_Y);
		setDefaultCloseOperation(DISPOSE_ON_CLOSE);
		setVisible(true);
	}
}

class PointsPanel extends JPanel
{
	/**
	 * serialVersionUID
	 */
	private static final long serialVersionUID = -5365893292526986749L;
	private static final int R = 3; // radius of points

	// record points on the panel
	ArrayList<Point> points = new ArrayList<Point>();

	ArrayList<Line> lines = new ArrayList<Line>();

	// help with finding duplicate points
	HashMap<Integer, HashSet<Integer>> coordMap = new HashMap<Integer, HashSet<Integer>>();

	public PointsPanel()
	{
		addMouseListener(new PointsPanelMouseListener());
	}

	class PointsPanelMouseListener extends MouseAdapter
	{
		public void mousePressed(MouseEvent e)
		{
			int x = e.getX();
			int y = e.getY();

			// if this point have an existence, then ignore it. Otherwise,
			// draw this point in the panel and add it into coordMap
			if (!exists(x, y))
			{
				// update points and coordMap
				points.add(new Point(x, y));

				if (coordMap.containsKey(x))
				{
					coordMap.get(x).add(y);
				}
				else
				{
					HashSet<Integer> ySet = new HashSet<Integer>();
					ySet.add(y);
					coordMap.put(x, ySet);
				}

				// draw point
				lines.clear();
				repaint();
			}
		}
	}

	public void paint(Graphics g)
	{
		super.paint(g);
		Graphics2D g2D = (Graphics2D) g;
		g2D.setColor(Color.DARK_GRAY);
		for (Point point : points)
		{
			int x = point.x > R ? point.x - R : 0;
			int y = point.y > R ? point.y - R : 0;

			g2D.fill(new Ellipse2D.Double(x, y, 2 * R + 1, 2 * R + 1));
		}

		g.setColor(Color.BLUE);
		for (Line line : lines)
		{
			g.drawLine(line.firPoint.x, line.firPoint.y, line.secPoint.x,
					line.secPoint.y);
		}
	}

	public void drawLine(ArrayList<NearestPoints> nearestPointsArr)
	{
		lines.clear();
		for (NearestPoints nearestPoints : nearestPointsArr)
		{
			lines.add(new Line(nearestPoints.firPoint, nearestPoints.secPoint));
		}

		// drawLine
		repaint();
	}

	private boolean exists(int x, int y)
	{
		if (coordMap.containsKey(x))
		{
			if (coordMap.get(x).contains(y))
			{
				return true;
			}
		}
		return false;
	}

	public ArrayList<Point> getPoints()
	{
		return points;
	}
}